//
//  StringsAndCharacters.swift
//  cnswift-demo
//
//  Created by wyang on 2022/7/21.
//  Copyright © 2022 YangWei. All rights reserved.
//

import Foundation

/// 字符串和字符
class StringsAndCharacters: SWDBaseObject {
 
    class func run() {
        
        let stringsAndCharacters = StringsAndCharacters()
        
        // 1.0
        stringsAndCharacters.stringLiterals()
        
        // 1.1
        stringsAndCharacters.multilineStringLiterals()
        
        // 1.2
        stringsAndCharacters.specialCharactersInStringLiterals()
        
        // 1.3
        stringsAndCharacters.extendedStringDelimiters()
        
        // 2.0
        stringsAndCharacters.initializingAnEmptyString()
        
        // 3.0
        stringsAndCharacters.stringMutability()
        
        // 5.0
        stringsAndCharacters.workingWithCharacters()
        
        // 6.0
        stringsAndCharacters.concatenatingStringsAndCharacters()
        
        // 7.0
        stringsAndCharacters.stringInterpolation()
        
        // 8.1
        stringsAndCharacters.unicodeScalarValues()
        
        // 8.2
        stringsAndCharacters.extendedGraphemeClusters()
        
        // 9.0
        stringsAndCharacters.countingCharacters()
        
        // 10.1
        stringsAndCharacters.stringIndices()
        
        // 10.2
        stringsAndCharacters.insertingAndRemoving()
        
        // 11
        stringsAndCharacters.substrings()
        
        // 12.1
        stringsAndCharacters.comparingStrings()
        
        // 12.2
        stringsAndCharacters.prefixAndSuffixEquality()
        
        // 13.0
        stringsAndCharacters.unicodeRepresentationsOfStrings()
        
        // 13.1
        stringsAndCharacters.utf8Representation()
        
        // 13.2
        stringsAndCharacters.utf16Representation()
        
        // 13.3
        stringsAndCharacters.unicodeScalarRepresentation()
    }
    
    /// 1.0 字符串字面量
    func stringLiterals() {
        
        let title = "1.0 字符串字面量";
        printStartLine(title)
        
        let someString = "Some string literal value"
        print(someString)
        
        printEndLine(title)
    }
    
    /// 1.1 多行字符串面量
    func multilineStringLiterals() {
        
        let title = "1.1 多行字符串面量";
        printStartLine(title)
        
        /* 多行字符串字面量是用三个双引号引起来的一系列字符：*/
        let quotation = """
        The White Rabbit put on his spectacles.  "Where shall I begin,
        please your Majesty?" he asked.
        
        "Begin at the beginning," the King said gravely, "and go on
        till you come to the end; then stop."
        """
        
        /*
         使用换行符（代码展示换行，内容显示不换行，不影响字符串内容）
         让代码易读，在行的末尾使用反斜杠（ \ ）：
         */
        let softWrappedQuotation = """
        The White Rabbit put on his spectacles.  "Where shall I begin, \
        please your Majesty?" he asked.
         
        "Begin at the beginning," the King said gravely, "and go on \
        till you come to the end; then stop."
        """
        print(softWrappedQuotation)
        
        /*
         让多行字符串字面量起始或结束于换行，可以在第一或最后一行写一个空行
         */
        let lineBreaks = """
         
        This string starts with a line break.
        It also ends with a line break.
         
        """
        print(lineBreaks)
        
        print("quotation:\n", quotation)
        print(quotation == generateQuotation())
        // Prints "true"
        
        printEndLine(title)
    }
    
    func generateQuotation() -> String {
        
        /*
         多行字符串可以缩进以匹配周围的代码。
         双引号（ """ ）前的空格告诉 Swift 其他行前应该有多少空白是需要忽略的
         */
      
        let quotation = """
            The White Rabbit put on his spectacles.  "Where shall I begin,
            please your Majesty?" he asked.
            
            "Begin at the beginning," the King said gravely, "and go on
            till you come to the end; then stop."
            """
        
        print("generateQuotation:\n", quotation)
        
        return quotation
    }
    
    /// 1.3 扩展字符串分隔符（#）
    func extendedStringDelimiters() {
        
        let title = "1.3 扩展字符串分隔符（#）";
        printStartLine(title)
        
        let threeMoreDoubleQuotationMarks = #"""
        Here are three more double quotes: """
        """#
        
        print(threeMoreDoubleQuotationMarks)
        
        printEndLine(title)
    }
    
    /// 2.0 初始化一个空字符串
    func initializingAnEmptyString() {
        
        let title = "2.0 初始化一个空字符串";
        printStartLine(title)
        
        var emptyString = ""               // empty string literal
        var anotherEmptyString = String()  // initializer syntax
        // these two strings are both empty, and are equivalent to each other
        
        /*
         确认一个 String值是否为空
         */
        if emptyString.isEmpty {
            print("Nothing to see here")
        }
        // prints "Nothing to see here"
        
        printEndLine(title)
    }
    
    /// 3.0 字符串可变性
    func stringMutability() {
        
        let title = "3.0 字符串可变性";
        printStartLine(title)
        
        /*
         把一个 String设置为变量（这里指可被修改），或者为常量（不能被修改）：
         */
        var variableString = "Horse"
        variableString += " and carriage"
        // variableString is now "Horse and carriage"
        print(variableString)
         
        let constantString = "Highlander"
//        constantString += " and another Highlander" // 编译错误❌
        // this reports a compile-time error - a constant string cannot be modified
        
        printEndLine(title)
    }
    
    /// 5.0 操作字符
    func workingWithCharacters() {
        
        let title = "5.0 操作字符";
        printStartLine(title)
        
        for character in "Dog!🐶️" {
            print(character)
        }
        // D
        // o
        // g
        // !
        // 🐶️
        
        /*
         可以通过提供 Character 类型标注从单个字符的字符串字面量创建一个独立的 Character 常量或者变量：
         */
        let exclamationMark: Character = "!"
        print("exclamationMark = ", exclamationMark)
        
        printEndLine(title)
    }
    
    /// 6.0 连接字符串和字符
    func concatenatingStringsAndCharacters() {
        let title = "6.0 连接字符串和字符";
        printStartLine(title)
        
        /* 使用加运算符（ +）*/
        let string1 = "hello"
        let string2 = " there"
        var welcome = string1 + string2
        print(welcome) // "hello there"
        
        /* 加赋值符号（ +=）*/
        var instruction = "look over"
        instruction += string2
        print(instruction) // "look over there"
        
        /* append()方法来可以给一个 String 变量的末尾追加 Character 值 */
        let exclamationMark: Character = "!"
        welcome.append(exclamationMark)
        print(welcome) // "hello there!"
        
        /* 多行文本使用 + ，在结束的 """  前保留一个空行，可以保持连接处开始新的一行效果 */
        let badStart = """
        one
        two
        """
        let end = """
        three
        """
        print(badStart + end)
        // Prints two lines:
        // one
        // twothree
         
        let goodStart = """
        one
        two
        
        """
        print(goodStart + end)
        // Prints three lines:
        // one
        // two
        // three
        
        printEndLine(title)
    }
    
    /// 7.0 字符串插值
    func stringInterpolation() {
        let title = "7.0 字符串插值";
        printStartLine(title)
        
        let multiplier = 3
        let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
        print(message) // "3 times 2.5 is 7.5"
        
        /*
         在扩展字符串分隔符中使用字符串插值的字符，要在反斜杠后使用匹配首尾井号数量的井号，才能使用插值：
         */
        print(#"Write an interpolated string in Swift using \(multiplier)."#)
        // "Write an interpolated string in Swift using \(multiplier)."

        print(#"6 times 7 is \#(6 * 7)."#)
        // "6 times 7 is 42."
        
        printEndLine(title)
    }
    
    /// 8.1 Unicode 标量
    func unicodeScalarValues() {
        let title = "8.1 Unicode 标量";
        printStartLine(title)
        
        // 打印 Unicode：
        let a = "\u{0061}"
        print(a)
                
        let chick = "\u{1F425}"
        print(chick)
        
        printEndLine(title)
    }
    
    /// 8.2 扩展字形集群
    func extendedGraphemeClusters() {
        let title = "8.2 扩展字形集群";
        printStartLine(title)
        
        /*
         扩展字形集群是一个或者多个有序的 Unicode 标量（当组合起来时）产生的单个人类可读字符
         支持标量组合
         */
        let eAcute: Character = "\u{E9}" // é
        let combinedEAcute: Character = "\u{65}\u{301}" // e followed by
        // eAcute is é, combinedEAcute is é
        print("eAcute         = \(eAcute)")
        print("combinedEAcute = \(combinedEAcute)")
        
        // 测试一下调整顺序的组合，标量组合有一定的顺序要求
//        let combinedEAcute2: Character = "\u{301}\u{65}" // 顺序不对，编译报错❌
//        print("combinedEAcute2 = \(combinedEAcute2)")
        
        
        let precomposed: Character = "\u{D55C}" // 한
        let decomposed: Character = "\u{1112}\u{1161}\u{11AB}" // ᄒ, ᅡ, ᆫ
        // precomposed is 한, decomposed is 한
        print("precomposed = \(precomposed)")
        print("decomposed  = \(decomposed)")
        
        /*
         扩展字形集群允许封闭标记的标量 (比如 COMBINING ENCLOSING CIRCLE, 或者说 U+20DD) 作为单一 Character值来圈住其他 Unicode 标量：
         下面的 \u{20DD} 圈就是封闭标量 还可以和 한 或 e 组合
         */
        let enclosedEAcute: Character = "\u{65}\u{20DD}"
        // enclosedEAcute is é⃝
        print("enclosedEAcute is \(enclosedEAcute)")
        
        /*
         区域指示符号的 Unicode 标量可以成对组合来成为单一的 Character 值
         如:
         REGIONAL INDICATOR SYMBOL LETTER U ( U+1F1FA):\u{1F1FA}
         REGIONAL INDICATOR SYMBOL LETTER S ( U+1F1F8):\u{1F1F8}
         */
        let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
        // regionalIndicatorForUS is 🇺🇸
        print("regionalIndicatorForUS is \(regionalIndicatorForUS)")
        
        printEndLine(title)
    }
    
    /// 9.0 字符统计
    func countingCharacters() {
        let title = "9.0 字符统计";
        printStartLine(title)
        
        /*
         计算字符串 Character 总数，使用字符串的 count 属性：
         */
        let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪"
        print("unusualMenagerie has \(unusualMenagerie.count) characters")
        // Prints "unusualMenagerie has 40 characters"
        
        /*
         字符串的创建和修改可能不会总是影响字符串的字符统计数。
         如下面 末尾拼接一个字符 "\u{301}"，只是改变了最后一个字符
         */
        var word = "cafe"
        print("the number of characters in \(word) is \(word.count)")
        // Prints "the number of characters in cafe is 4"
         
        word += "\u{301}"    // COMBINING ACUTE ACCENT, U+0301
         
        print("the number of characters in \(word) is \(word.count)")
        // Prints "the number of characters in café is 4"
        
        printEndLine(title)
    }
    
    /// 10.1 字符串索引
    func stringIndices() {
        let title = "10.1 字符串索引";
        printStartLine(title)
        
        /*
         使用下标语法来访问 String 索引中的特定 Character：
         */
        let greeting = "Guten Tag!"
        // G
        let c1 = greeting[greeting.startIndex]
        print("\(greeting).startIndex = ", c1)
        
        // !
        let c2 = greeting[greeting.index(before: greeting.endIndex)]
        print("\(greeting).index(before: greeting.endIndex) = ", c2)
        
        // u
        let c3 = greeting[greeting.index(after: greeting.startIndex)]
        print("\(greeting).index(after: greeting.startIndex) = ", c3)
        
        let index = greeting.index(greeting.startIndex, offsetBy: 7)
        // a
        let c4 = greeting[index]
        print("\(greeting).index(greeting.startIndex, offsetBy: 7) = ", c4)
        
        
        /*
         尝试访问的 Character 如果索引位置在字符串范围之外，就会触发运行时错误。
         */
//        greeting[greeting.endIndex] // 运行后 ❌
//        greeting.index(after: endIndex) // 编译 ❌
        
        /*
         使用 indices 属性来访问字符串中每个字符的索引。
         */
        for index in greeting.indices {
            print("\(greeting[index]) ", terminator: "")
        }
        // Prints "G u t e n   T a g ! "
        
        print("")
        printEndLine(title)
    }
    
    /// 10.2 插入和删除
    func insertingAndRemoving() {
        let title = "10.2 插入和删除";
        printStartLine(title)
        
        /*
         使用 insert(_:at:)方法给字符串的特定索引位置插入字符
         使用 insert(contentsOf:at:) 方法冲入另一个字符串的内容到特定的索引
         */
        var welcome = "hello"
        welcome.insert("!", at: welcome.endIndex)
        // welcome now equals "hello!"
        print(welcome)
         
        welcome.insert(contentsOf: " there", at: welcome.index(before: welcome.endIndex))
        // welcome now equals "hello there!"
        print(welcome)
        
        /*
         使用 remove(at:)方法从字符串的特定索引位置移除字符
         使用 removeSubrange(_:) 方法移除一小段特定范围的字符串
         */
        welcome.remove(at: welcome.index(before: welcome.endIndex))
        // welcome now equals "hello there"
        print(welcome)
         
        let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
        welcome.removeSubrange(range)
        // welcome now equals "hello"
        print(welcome)
        
        printEndLine(title)
    }
    
    /// 11.0 子字符串
    func substrings() {
        let title = "11.0 子字符串";
        printStartLine(title)
        
        /*
         与字符串不同，在字符串上执行动作的话应该使用子字符串执行短期处理。
         当想要把结果保存得长久一点时，需要把子字符串转换为 String 实例
         */
        let greeting = "Hello, world!"
        let index = greeting.index(of: ",") ?? greeting.endIndex
        let beginning = greeting[..<index]
        // beginning is "Hello"
         
        // Convert the result to a String for long-term storage.
        let newString = String(beginning)
        print(newString)
        
        printEndLine(title)
    }
    
    /// 12.1 字符串和字符相等性
    func comparingStrings() {
        let title = "12.1 字符串和字符相等性";
        printStartLine(title)
        
        /*
         同比较运算符，字符串和字符相等使用“等于”运算符 ( ==) 和“不等”运算符 ( !=)进行检查：
         */
        let quotation = "We're a lot alike, you and I."
        let sameQuotation = "We're a lot alike, you and I."
        if quotation == sameQuotation {
            print("These two strings are considered equal")
        }
        // prints "These two strings are considered equal"
        
        
        /*
         两个扩展字形集群都是表示字符é的合法方式，所以它们被看做规范化相等
         */
        // "Voulez-vous un café?" using LATIN SMALL LETTER E WITH ACUTE
        let eAcuteQuestion = "Voulez-vous un caf\u{E9}?"
        print("eAcuteQuestion = ", eAcuteQuestion)
         
        // "Voulez-vous un café?" using LATIN SMALL LETTER E and COMBINING ACUTE ACCENT
        let combinedEAcuteQuestion = "Voulez-vous un caf\u{65}\u{301}?"
        print("combinedEAcuteQuestion = ", combinedEAcuteQuestion)
        
        if eAcuteQuestion == combinedEAcuteQuestion {
            print("These two strings are considered equal")
        }
        // prints "These two strings are considered equal"
        
        /*
         LATIN CAPITAL LETTER A ( U+0041, 或者说 "A")在英语当中是不同于俄语的 CYRILLIC CAPITAL LETTER A ( U+0410,或者说 "А")的。
         字符看上去差不多，但是它们拥有不同的语言意义
         ⚠️ 两个A不一样
         */
        let latinCapitalLetterA: Character = "\u{41}"
        print("latinCapitalLetterA = ", latinCapitalLetterA)
         
        let cyrillicCapitalLetterA: Character = "\u{0410}"
        print("cyrillicCapitalLetterA = ", cyrillicCapitalLetterA)
         
        if latinCapitalLetterA != cyrillicCapitalLetterA {
            print("These two characters are not equivalent")
        }
        // prints "These two characters are not equivalent"
        
        printEndLine(title)
    }
    
    /// 12.2 前缀和后缀相等性
    func prefixAndSuffixEquality() {
        let title = "12.2 前缀和后缀相等性";
        printStartLine(title)
        
        let romeoAndJuliet = [
            "Act 1 Scene 1: Verona, A public place",
            "Act 1 Scene 2: Capulet's mansion",
            "Act 1 Scene 3: A room in Capulet's mansion",
            "Act 1 Scene 4: A street outside Capulet's mansion",
            "Act 1 Scene 5: The Great Hall in Capulet's mansion",
            "Act 2 Scene 1: Outside Capulet's mansion",
            "Act 2 Scene 2: Capulet's orchard",
            "Act 2 Scene 3: Outside Friar Lawrence's cell",
            "Act 2 Scene 4: A street in Verona",
            "Act 2 Scene 5: Capulet's mansion",
            "Act 2 Scene 6: Friar Lawrence's cell"
        ]
        
        /*
         使用 hasPrefix(_:)方法操作 romeoAndJuliet 数组来计算第一场场景的数量
         */
        var act1SceneCount = 0
        for scene in romeoAndJuliet {
            if scene.hasPrefix("Act 1 ") {
                act1SceneCount += 1
            }
        }
        print("There are \(act1SceneCount) scenes in Act 1")
        // Prints "There are 5 scenes in Act 1"
        
        /*
         使用 hasSuffix(_:)方法来计算与 Capulet’s mansion 和 Friar Lawrence’s cell 两个地方相关的场景数量：
         */
        var mansionCount = 0
        var cellCount = 0
        for scene in romeoAndJuliet {
            if scene.hasSuffix("Capulet's mansion") {
                mansionCount += 1
            } else if scene.hasSuffix("Friar Lawrence's cell") {
                cellCount += 1
            }
        }
        print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
        // Prints "6 mansion scenes; 2 cell scenes"
        
        
        printEndLine(title)
    }
    
    /// 13.0 字符串的 Unicode 表示法
    func unicodeRepresentationsOfStrings() {
        let title = "13.0 字符串的 Unicode 表示法";
        printStartLine(title)
        
        /*
         这个字符串由字符 D ， o ， g ， ‼  ( DOUBLE EXCLAMATION MARK, 或者说 Unicode 标量 U+203C)以及 🐶 字符( DOG FACE , 或者说 Unicode 标量 U+1F436)组成
         */
        let dogString = "Dog‼🐶"
        print(dogString)
        
        printEndLine(title)
    }
    
    /// 13.1 UTF-8 表示法
    func utf8Representation() {
        let title = "13.1 UTF-8 表示法";
        printStartLine(title)
        
        let dogString = "Dog‼🐶"
        
        for codeUnit in dogString.utf8 {
            print("\(codeUnit) ", terminator: "")
        }
        print("")
        // 68 111 103 226 128 188 240 159 144 182
        
        printEndLine(title)
    }
    
    /// 13.2 UTF-16 表示法
    func utf16Representation() {
        let title = "13.2 UTF-16 表示法";
        printStartLine(title)
        
        let dogString = "Dog‼🐶"
        
        for codeUnit in dogString.utf16 {
            print("\(codeUnit) ", terminator: "")
        }
        print("")
        // Prints "68 111 103 8252 55357 56374 "
        
        printEndLine(title)
    }
    
    /// 13.3 Unicode 标量表示法
    func unicodeScalarRepresentation() {
        let title = "13.3 Unicode 标量表示法";
        printStartLine(title)
        
        let dogString = "Dog‼🐶"
        
        for scalar in dogString.unicodeScalars {
            print("\(scalar.value) ", terminator: "")
        }
        print("")
        // Prints "68 111 103 8252 128054 "
        
        /*
         每一个 UnicodeScalar 值同样可以用来构造新的 String 值
         如，使用字符串插值：
         */
        for scalar in dogString.unicodeScalars {
            print("\(scalar) ")
        }
        // D
        // o
        // g
        // ‼
        // 🐶
        
        printEndLine(title)
    }
    
    /// 1.2 字符串字面量里的特殊字符
    func specialCharactersInStringLiterals() {
        
        let title = "1.2 字符串字面量里的特殊字符";
        printStartLine(title)
        
        /*
         wiseWords 常量包含了两个转义双引号字符。
         dollarSign， blackHeart 和 sparklingHeart常量展示了 Unicode 标量格式：
         */
        let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
        // "Imagination is more important than knowledge" - Einstein
        let dollarSign = "\u{24}" // $, Unicode scalar U+0024
        let blackHeart = "\u{2665}" // ♥, Unicode scalar U+2665
        let sparklingHeart = "\u{1F496}" // 💖️, Unicode scalar U+1F496
        
        print(wiseWords)
        print(dollarSign)
        print(blackHeart)
        print(sparklingHeart)
        
        /*
         多行字符串字面量中可以包含双引号（ " ）而不需转义。
         要在多行字符串中包含文本 """ ，转义至少一个双引号
         */
        let threeDoubleQuotationMarks = """
        Escaping the first quotation mark \"""
        Escaping all three quotation marks \"\"\"
        """
        print(threeDoubleQuotationMarks)

        printEndLine(title)
    }
}
